/**
 * Copyright (c) 2009-2011, chunquedong(YangJiandong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 
 * History:
 *     2010-05-05  Jed Young  Creation
 */
package chunmap.model.elem;

import chunmap.model.coord.Position;
import chunmap.model.coord.Coordinate2D;
import chunmap.util.math.MyDouble;

/**
 * 直线
 * 
 * @author chunquedong
 * 
 */
public class Line {

	private double k;// 斜率
	private double b;// 截距
	private boolean isVertical = false;

	/**
	 * 两点式
	 */
	public Line(Position p1, Position p2) {
		double dy = p2.getY() - p1.getY();
		double dx = p2.getX() - p1.getX();

		if (p1.equals(p2))
			throw new IllegalArgumentException(
					"need tow different points to make one line");
		// 竖直线时
		if (dx == 0) {
			k = Double.POSITIVE_INFINITY;
			b = p1.getX();// b变为x轴的截距
			isVertical = true;
		} else {
			k = dy / dx;
			b = p1.getY() - k * p1.getX();
		}

	}

	/**
	 * 截距式
	 */
	public Line(double k, double b) {
		if (Double.isInfinite(k))
			isVertical = true;
		this.k = k;
		this.b = b;
	}

	/**
	 * 点斜式
	 */
	public Line(Position p, double k) {
		if (Double.isInfinite(k)) {
			k = Double.POSITIVE_INFINITY;
			b = p.getX();
			isVertical = true;
		} else {
			b = p.getY() - k * p.getX();
			this.k = k;
		}
	}

	// ------------------------------------------------------------

	public boolean isVertical() {
		return isVertical;
	}

	public double getY(double x) {
		if (isVertical)
			return Double.NaN;// 竖直

		return k * x + b;
	}

	public double getX(double y) {
		if (isVertical)
			return b;// 竖直
		if (k == 0)
			return Double.NaN;// 水平

		return (y - b) / k;
	}

	/**
	 * 斜率
	 * 
	 * @return
	 */
	public double getK() {
		if (isVertical)
			return Double.POSITIVE_INFINITY;
		return k;
	}

	/**
	 * 截距
	 * 
	 * @return
	 */
	public double getB() {
		return b;
	}

	/**
	 * 垂直线的斜率
	 * 
	 * @return
	 */
	public double getVerticalK() {
		if (isVertical)
			return 0;
		else if (k == 0)
			return Double.POSITIVE_INFINITY;
		return -1d / k;
	}

	// ------------------------------------------------------------

	/**
	 * 两线交点，平行返回null
	 */
	public Position crossPoint(Line l2) {
		double k1 = this.getK();
		double b1 = this.getB();
		double k2 = l2.getK();
		double b2 = l2.getB();

		if (this.isVertical() && l2.isVertical()) {
			return null;
		} else if (this.isVertical()) {
			double y = l2.getY(this.b);
			return new Coordinate2D(this.b, y);
		} else if (l2.isVertical()) {
			double y = this.getY(l2.b);
			return new Coordinate2D(l2.b, y);
		} else if (k1 == k2) {
			return null;// 平行
		} else if (k1 == 0) {
			double x = l2.getX(this.b);
			return new Coordinate2D(x, this.b);
		} else if (k2 == 0) {
			double x = this.getX(l2.b);
			return new Coordinate2D(x, l2.b);
		}

		double x = (b1 - b2) / (k2 - k1);
		double y = this.getY(x);

		return new Coordinate2D(x, y);
	}

	/**
	 * 点到直线的距离
	 * 
	 * @param point
	 * @return
	 */
	public double distance(Position point) {
		Line vertical = new Line(point, getVerticalK());
		Position crossP = crossPoint(vertical);
		return point.distance2D(crossP);
	}

	/**
	 * 点是否在线上,近似值
	 * 
	 * @param p
	 * @return
	 */
	public boolean onLine(Position p) {
		// 线垂直时
		if (isVertical()) {
			return p.getX() == getB();
		}

		double expectedY = getY(p.getX());
		return MyDouble.approximateEquals(expectedY, p.getY());
	}
}